/**
 * 
 */
package pl.koziolekweb.pmt;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 
 * Klasa służy do uruchamiania metod prywatnych i ustawiania pól prywatnych w
 * testach jednostkowych.
 * 
 * @author bartlomiejk
 * 
 */
public class PrivateMemberTester {

	/**
	 * Uruchamia wskazaną metodę. Metoda może być prywatna.
	 * 
	 * @param methodName
	 *            nazwa metody uruchamianej.
	 * @param owner
	 *            obiekt na rzecz, ktorego uruchamiana jest metoda.
	 * @param args
	 *            argumenty metody.
	 * @param clazz
	 *            klasa do której należy metoda.
	 * @param parameterTypes
	 *            tablica klas argumentów metody.
	 * @return to samo co wywoływana metoda.
	 * @throws Throwable
	 *             Błąd jaki został zwrocony przez wywolywaną metodę lub błąd
	 *             wywołania metody.
	 */
	public static Object runMethod(final String methodName, final Object owner,
			final Object[] args, final Class<?> clazz,
			final Class<?>... parameterTypes) throws Throwable {
		Object o = null;
		try {
			Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
			method.setAccessible(true);
			o = method.invoke(owner, args);
			return o;
		} catch (Throwable e) {
			Throwable t = null;
			if (e.getCause() != null) {
				t = (Throwable) e.getCause();
			} else {
				t = e;
			}
			throw t;
		}
	}

	/**
	 * Pobiera wartość danego pola. Pole może być prywatne.
	 * 
	 * @param fieldName
	 *            nazwa pola.
	 * @param owner
	 *            obiekt z ktorego czerpane są informacje o polu.
	 * @param ownerClass
	 *            Klasa do której nalezy pole.
	 * @return wartość pola jako {@link Object}.
	 * @throws NoSuchFieldException
	 *             jeżeli podane pole nie istnieje.
	 * @throws IllegalAccessException
	 *             nie powinno być wywalone.
	 */
	public static Object getPrivateFiled(final String fieldName,
			final Object owner, final Class<?> ownerClass)
			throws NoSuchFieldException, IllegalAccessException {

		Field declaredField = ownerClass.getDeclaredField(fieldName);
		declaredField.setAccessible(true);
		return declaredField.get(owner);
	}

	/**
	 * Metoda pozwala na ustawienie prywatnego pola.
	 * 
	 * @param fieldName
	 *            nazwa pola.
	 * @param owner
	 *            obiekt dla którego chcemy ustawić pole.
	 * @param value
	 *            nowa wartość pola.
	 * @param ownerClass
	 *            klasa do której należy pole.
	 * @throws NoSuchFieldException
	 *             jeżeli podane pole nie istnieje.
	 * @throws IllegalAccessException
	 *             nie powinno być wywalone.
	 */
	public static void setPrivateField(final String fieldName,
			final Object owner, final Object value, final Class<?> ownerClass)
			throws NoSuchFieldException, IllegalAccessException {
		Field declaredField = ownerClass.getDeclaredField(fieldName);
		declaredField.setAccessible(true);
		declaredField.set(owner, value);
	}
}
